#include "buffer.h"
#include "string.h"
#include "stdlib.h"

FEBuffer *newFileFEBuffer(const char *name, const char *file){
    FILE *fp = fopen(file, "r");
    if(!fp) return nullptr;

    FEBuffer *buffer = (FEBuffer *)malloc(sizeof(FEBuffer));
    memset(buffer, 0, sizeof(FEBuffer));
    buffer->name = _strdup(name);
    buffer->file = fp;
    fseek(fp, 0, SEEK_END);
    buffer->data_len = ftell(fp);    // 获取文件长度
    return buffer;
}

FEBuffer *newMemoryFEBuffer(const char *name, FEBuffer *parent, uint64_t parent_offset, uint64_t parent_len, char *ptr, uint64_t len){
    FEBuffer *buffer = (FEBuffer *)malloc(sizeof(FEBuffer));
    memset(buffer, 0, sizeof(FEBuffer));

    buffer->name = _strdup(name);
    buffer->parent = parent;
    buffer->parent_bit_offset = parent_offset;
    buffer->parent_bit_len = parent_len;
    buffer->ptr = ptr;
    buffer->data_len = buffer->ptr_len = buffer->cache_len = len;
    return buffer;
}

void deleteFEBuffer(FEBuffer *buffer){
    if(buffer->name) free(buffer->name);
    if(buffer->file) fclose(buffer->file);
    if(buffer->ptr) free(buffer->ptr);
    free(buffer);
    return;
}

#define MIN_CACHE_LEN 128*1024

char *fetchFEBuffer(FEBuffer *buffer, uint64_t offset, uint64_t len){
    if ((offset + len) > buffer->data_len)
        return NULL;

    if (offset >= buffer->ptr_offset && (offset + len) <= (buffer->ptr_offset + buffer->cache_len))
        return buffer->ptr + offset - buffer->ptr_offset;

    /* 根据最新请求cache数据 */
    buffer->cache_len = 0;
    if (buffer->ptr_len < len || (buffer->ptr_len > MIN_CACHE_LEN && buffer->ptr_len > len )) {
        free(buffer->ptr);
        buffer->ptr = NULL;
        buffer->ptr_len = 0;

        buffer->ptr_len = len > MIN_CACHE_LEN ? len : MIN_CACHE_LEN;
        buffer->ptr = (char *)malloc(buffer->ptr_len);
    }

    buffer->cache_len = (buffer->data_len - offset) < buffer->ptr_len ? (buffer->data_len - offset) : buffer->ptr_len;
    fseek(buffer->file, offset, SEEK_SET);
    fread(buffer->ptr, 1, buffer->cache_len, buffer->file);
    buffer->ptr_offset = offset;

    return buffer->ptr;
}
